Skip to content

Add parallel testnet build+deploy to the release pipeline - #9

Merged
gpmayorga merged 7 commits into
mainfrom
claude/testnet-parallel-deployment-7ieajb
Aug 20, 2026
Merged

Add parallel testnet build+deploy to the release pipeline#9
gpmayorga merged 7 commits into
mainfrom
claude/testnet-parallel-deployment-7ieajb

Conversation

@gpmayorga

@gpmayorga gpmayorga commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

What

Two changes to the release pipeline, plus a naming cleanup that touches every environment.

1. A demo leg on prerelease. Alongside the mainnet staging deploy, prereleased now runs a second, testnet-mode build and ships it to a standalone Worker (wrangler deploy --env demo). It is enabled by the caller's wrangler.toml: a detect-demo job parses it and turns the leg on when an [env.demo] section exists, taking the deployment URL from that section's custom_domain route. No caller input, so adopting it in an app is a config-only change. tomllib, not grep, so comments, key order and quoting can't change the answer; malformed TOML fails loudly instead of reading as "no demo".

2. Promotion takes no tag. app-promote-production.yml resolves GitHub's latest release (excludes drafts and prereleases) and promotes that, so there's nowhere to type an arbitrary tag. Promoting a specific non-latest tag is app-rollback.yml's job.

3. One environment name per tier, used for the pipeline input, the wrangler env and the GitHub Environment:

Tier Was Now Worker shared with
PR build dev / GH preview preview nightly
tracks main demo nightly preview
release tag, testnet testnet demo
release tag, mainnet staging staging production
promoted prod prod staging

--mode testnet is untouched — that names the chain, not the environment. Only two names still differ from their wrangler env, both because two tiers share a Worker: stagingprod (version alias) and previewnightly (unpromoted version). That collapses the old four-branch mapping in deploy-app to two special cases.

This also retires public-demo, which nothing consumes once apps-management moves to the demo leg, and which would otherwise collide with the new wrangler [env.demo].

Contract changes

  • Inputs: demo-build-args (default --mode testnet), demo-build-env. Removed: deploy-public-demo, and promotion's tag.
  • Output: demo-url (replaces testnet-url).
  • app-rollback.yml: environment: demo replaces testnet; the AUTHORIZED_DEPLOYERS gate covers every target.
  • build-demo declares environment: demo, as build does for staging, so a protection rule gates the build and not just the deploy.

New "Adding an environment" README section and matching CLAUDE.md bullets record the conventions: derive targets from app-owned config, parse with tomllib, and keep one name per tier.

⚠️ This is a coordinated cutover, not an additive change

The earlier revision of this PR was purely additive; the environment rename is not. The library reads the apps' wrangler env names at runtime, so it breaks in whichever direction lands first — the consumer PRs are currently red for exactly this reason (No environment found in configuration with name "dev").

Merge this PR first, then both consumer PRs straight after. Re-running their checks after this lands turns them green.

Only the dev pipeline is exposed in that window: [env.prod] is untouched and stagingprod is unchanged, so staging, promotion and production keep working. Worst case in the gap is a failed preview or nightly deploy in an app repo whose PR hasn't merged yet.

Verification

  • actionlint + yamllint (relaxed, line-length off — same as lib-ci) clean.
  • The detector was run against real fixtures: both apps' updated wrangler.toml (enabled, correct URLs), today's app main with no [env.demo] (disabled — nothing changes for a consumer until it opts in), [env.demo] with no custom domain (enabled, falls back to wrangler's URL), missing file (disabled), malformed TOML (fails with a clear ::error::).
  • Every caller with: key cross-checked against this workflow's contract.
  • Still needs a live throwaway-prerelease after merge: internal composite refs are @main, so deploy-app's new mapping only takes effect once this lands.

Consumers

  • centrifuge/apps-invest#270 — [env.demo] → demo.app.centrifuge.io
  • centrifuge/apps-management#1155 — replaces public-demo → demo.manage.centrifuge.io

Both also need a one-time GitHub Environment rename (demonightly) before their first prerelease; details in those PRs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ

@gpmayorga
gpmayorga force-pushed the claude/testnet-parallel-deployment-7ieajb branch from 639a146 to fbc03f6 Compare July 17, 2026 00:40
On prereleased events, callers can now opt into a second, testnet-mode
build (deploy-testnet: true) that deploys to a standalone testnet Worker
via a new 'testnet' environment in deploy-app (direct wrangler deploy
--env testnet). The testnet build uploads its own run artifact
(<app-name>-testnet-build-<sha>) and attaches its own release bundle
(<bundle-prefix>-testnet-bundle<tag>.zip), which also makes testnet
rollbacks possible: app-rollback.yml gains a 'testnet' environment that
redeploys that bundle.

New release-pipeline inputs: deploy-testnet, testnet-build-args
(default --mode testnet), testnet-build-env, testnet-url; new output:
testnet-url.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
@gpmayorga
gpmayorga force-pushed the claude/testnet-parallel-deployment-7ieajb branch from fbc03f6 to 475ed8c Compare August 18, 2026 18:00
Drops the deploy-testnet and testnet-url inputs. A new detect-testnet job
parses the caller's wrangler.toml and enables the testnet build+deploy when
an [env.testnet] section exists, taking the GitHub deployment URL from that
section's custom_domain route.

The inputs duplicated a fact wrangler.toml already had to state: an app
cannot deploy a testnet Worker without declaring it there, so requiring a
second "yes I want testnet" flag in the caller workflow only added a place
to drift — and forced every consumer to open a workflow PR to adopt the
feature. Deriving it means adopting testnet is a config change in the app
repo, and because callers pass no inputs an older main would reject, their
PRs no longer have to land after this one.

Parsed with tomllib, not grep, so comments, key order and quoting cannot
change the answer. A malformed wrangler.toml fails the job loudly rather
than reading as "no testnet". actions/deploy-app still greps for the prod
Worker name; that predates this and should follow, but it sits on the gated
promotion path so it is left alone here.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
@gpmayorga
gpmayorga marked this pull request as ready for review August 19, 2026 17:45
@gpmayorga
gpmayorga requested a review from hieronx as a code owner August 19, 2026 17:45
claude added 3 commits August 19, 2026 23:28
app-promote-production.yml no longer takes a tag input. A new resolve-tag
job looks up GitHub's own "latest release" (GET .../releases/latest, which
excludes drafts and prereleases) and every downstream step — the ancestry
check, the staged-version lookup, the Slack messages — uses that instead
of inputs.tag.

The tag a `released` event just staged is GitHub's latest release at that
exact moment, by construction: prereleased builds it, and flipping the
release out of prerelease (the `released` event) is what fires the Slack
notification asking someone to promote in the first place. So deriving it
loses no legitimate case, while removing the only place an operator could
type an arbitrary tag into this workflow. That capability now only exists
in the already-gated app-rollback.yml, which is what it's for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
Removes design-history asides (the abandoned deploy-testnet/testnet-url
inputs), restatements of what the YAML already shows, and justification
prose that CLAUDE.md already carries as durable guidance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
One name per tier, used for the pipeline input, the wrangler env and the
GitHub Environment. The main-merge deploy becomes `nightly` (it tracks main)
and the release-tag testnet deploy becomes `demo` (it is the thing you show
someone). `--mode testnet` is untouched: that names the chain, not the
environment.

Only two names still differ from their wrangler env, both because two tiers
share one Worker: staging is a version alias on the prod Worker, and PR
previews are unpromoted versions of the nightly Worker. That collapses the
old four-branch mapping in deploy-app to two special cases.

Retires public-demo, which nothing consumes once apps-management moves to
the demo leg, and which would otherwise collide with the new wrangler
[env.demo].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
The Slack notice, its failure message and the step summary all handed out
`gh workflow run promote-production.yml ... -f tag=<TAG>`. That input no
longer exists, so gh rejects the call with HTTP 422 — the notification was
telling deployers to run a command that cannot work.

Drops the input from the command and says in the message that promotion
targets GitHub's latest release, so the reader knows to release the tag
first rather than passing it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZs3qvSwpTvBZEPSqfXQzQ
@gpmayorga
gpmayorga merged commit 78fc12b into main Aug 20, 2026
3 checks passed
@gpmayorga
gpmayorga deleted the claude/testnet-parallel-deployment-7ieajb branch August 20, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants